﻿/*
 * OpenDoPE Word Add-In
 * Copyright (C) 2010-2011  Plutext Pty Ltd
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * The GPL obliges you to publish the source code of any program
 * you distribute which includes this code. If you don't want
 * to do that, you can ask Plutext for a commercial license.
 * 
 * If you would like to contribute your code back to this project,
 * please contact Plutext for the contributor agreement.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * 
 * For details, see http://www.gnu.org/licenses/gpl-2.0.html
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Word = Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Word;
using Microsoft.Office.Tools.Word.Extensions; // for VSTOObject
using NLog;
using NLog.Config;


namespace OpenDoPE_Wed
{
    //[System.Diagnostics.DebuggerNonUserCodeAttribute()]
    public partial class ThisAddIn
    {

        public Word.Application app;

        static Logger log;

        static ThisAddIn()
        {
            NLog.Config.LoggingConfiguration config = new NLog.Config.LoggingConfiguration();
            NLog.Targets.Target t;
            //System.Diagnostics.Trace.WriteLine(System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
            // eg file:///C:/Users/jharrop/Documents/Visual Studio 2010/Projects/com.plutext.search/com.plutext.search.main/bin/Debug/com.plutext.search.main.DLL
            if (System.Reflection.Assembly.GetExecutingAssembly().CodeBase.Contains("Debug"))
            {
                t = new NLog.Targets.DebuggerTarget();
                ((NLog.Targets.DebuggerTarget)t).Layout = "${callsite} ${message}";
            }
            else
            {
                t = new NLog.Targets.FileTarget();
                ((NLog.Targets.FileTarget)t).FileName = System.IO.Path.GetTempPath() + "plutext.txt";
                //// Win 7:  C:\Users\jharrop\AppData\Local\Temp\
                //System.Diagnostics.Trace.WriteLine("TEMP: " + System.IO.Path.GetTempPath());
                ((NLog.Targets.FileTarget)t).AutoFlush = true;
            }
            //ILayout layout = new NLog.Layout("${longdate} ${callsite} ${level} ${message}");
            //NLog.LayoutCollection lc = new NLog.LayoutCollection();
            //lc.Add(layout);
            ////t.GetLayouts().Add(layout);
            //t.PopulateLayouts(lc);

            config.AddTarget("ds", t);
            config.LoggingRules.Add(new NLog.Config.LoggingRule("*", LogLevel.Trace, t));
            LogManager.Configuration = config;
            log = LogManager.GetLogger("OpenDoPE_Wed");
            log.Info("Logging operational.");
        }


        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {

            app = this.Application;

            // Event handler for NewDocument
            //((Word.ApplicationEvents4_Event)app).NewDocument +=
            //    new Word.ApplicationEvents4_NewDocumentEventHandler(App_NewDocument);

            // (Application level) Event hander for DocumentOpen
            app.DocumentOpen +=
                new Word.ApplicationEvents4_DocumentOpenEventHandler(App_DocumentOpen);

            app.DocumentBeforeClose += 
                new Word.ApplicationEvents4_DocumentBeforeCloseEventHandler(app_DocumentBeforeClose);


            app.DocumentBeforeSave +=
                new Word.ApplicationEvents4_DocumentBeforeSaveEventHandler(App_DocumentBeforeSave);


            /*
             * Better for ribbon invalidate purposes to use WindowActive, since
             * in common situations, what fires is:
             * 
             *   WA (then sometimes DC)
             * 
             * or 
             * 
             *   DC then WA then DC again
             * 
             */
            //((Word.ApplicationEvents4_Event)app).DocumentChange += new Word.ApplicationEvents4_DocumentChangeEventHandler(ThisAddIn_DocumentChange);
            //((Word.ApplicationEvents4_Event)app).WindowActivate += new Word.ApplicationEvents4_WindowActivateEventHandler(ThisAddIn_WindowActivate);

            ((Word.ApplicationEvents4_Event)app).WindowSelectionChange += new Word.ApplicationEvents4_WindowSelectionChangeEventHandler(ThisAddIn_WindowSelectionChange);

            //GlobalErrorHandler.Ensure();


        }

        void App_DocumentBeforeSave(Word.Document Doc, ref bool saveAsUI, ref bool Cancel)
        {
            log.Debug(".. in before save event..");

            // Save the user's custom xml if the xml editor is dirty
            WedTaskPane wtp = Globals.ThisAddIn.getTaskPane();
            if (wtp != null)
            {
                wtp.saveXmlEditorContents();
            }

        }

        /// <summary>
        /// In docx before close, note the associated ctp
        /// </summary>
        private Microsoft.Office.Tools.CustomTaskPane pendingCloseCTP = null;
        void app_DocumentBeforeClose(Word.Document Doc, ref bool Cancel)
        {

            Document extendedDocument = Application.ActiveDocument.GetVstoObject(Globals.Factory);
            log.Debug(" = " + extendedDocument.Name);

            if (extendedDocument.Tag != null
                && extendedDocument.Tag is Microsoft.Office.Tools.CustomTaskPane)
            {
                pendingCloseCTP = (Microsoft.Office.Tools.CustomTaskPane)extendedDocument.Tag;
                log.Debug(".. taskpane identified for closing");
            }
            else
            {
                log.Debug(".. no associated taskpane to close");
            }

        }

        /*
         * Bug: if you cancel the close dialog on a docx which has a ctp,
         * and then close some other docx (which also has a ctp, since event
         * only fires in that case); the ctp belonging to the first 
         * docx will be closed!
         * 
         * May be able to prevent this by storing a reference to the docx
         * in the ctp, then doing something like doc.Name.  
         * If this throws exception "Object has been deleted", then
         * we know we're good to close the CTP.  If it doesn't, 
         * then it is probably the wrong document.
         * 
         * Implemented this.  But in hindsight, it shouldn't be
         * a problem, because extendedDocument_Shutdown should
         * only fire for docx which has a wedtaskpane.  
         * 
         * But maybe there are exotic cases, so keep the code...
         */

        /// <summary>
        /// Close the Custom Task Pane when the docx closes
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void extendedDocument_Shutdown(object sender, EventArgs e)
        {
            System.Diagnostics.Trace.WriteLine("in document shutdown");

            // sender is Microsoft.Office.Tools.Word.DocumentImpl
            
            //Document documentClosing = (Document)sender;
            //log.Debug("Document closing: " + documentClosing.Name); // Object has been deleted            

            if (pendingCloseCTP!=null)
            {
                try
                {
                    WedTaskPane wedTaskPane = (WedTaskPane)pendingCloseCTP.Control;
                    string foo = wedTaskPane.associatedDocument.Name;
                    log.Debug(".. cowardly refusing to close taskpane!");
                }
                catch (Exception ex)
                {
                    log.Error(ex.Message);
                    removeTaskPane(pendingCloseCTP);
                    pendingCloseCTP = null;
                    log.Debug(".. taskpane also closed");
                }
            }
            else
            {
                log.Debug(".. no associated taskpane to close");
            }

        }

        protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
        {

             return new Ribbon();
        }

/*
        void App_NewDocument(Word.Document document)
        {
            //System.Windows.Forms.MessageBox.Show("new doc");
            log.Debug("New doc event fired.");
            // new Microsoft.Office.Interop.Word.Document() doesn't make this fire!!!


            //document.Activate();

        }
 */ 

        void App_DocumentOpen(Word.Document document)
        {
            // AutoExec macro (if present) and this event will both run,
            // but where there is an auto exec macro, 
            // only if the user runs the AutoExec macro.
            // The AutoExec macro runs first, then this.

            log.Debug("App_DocumentOpen event fired.");
            //MessageBox.Show("App_DocumentOpen event fired.");


            createCTP(document);

            document.Activate();

            // Set state to match this docx
            Ribbon.myInvalidate();


            log.Debug("Open event handler finished");

        }

        public Microsoft.Office.Tools.CustomTaskPane createCTP(Word.Document document)
        {


            // Does this document contains the custom xml parts?
            // If so, add the task pane.
            Office.CustomXMLPart xpathsPart = null;
            Office.CustomXMLPart conditionsPart = null;
            Office.CustomXMLPart questionsPart = null;
            Office.CustomXMLPart componentsPart = null;
            List<Office.CustomXMLPart> cxp = new List<Office.CustomXMLPart>();

            foreach (Office.CustomXMLPart cp in document.CustomXMLParts)
            {
                log.Info("cxp: " + cp.DocumentElement + ", " + cp.NamespaceURI
                    + ", " + cp.Id);

                if (cp.NamespaceURI.Equals(Namespaces.XPATHS))
                {
                    xpathsPart = cp;
                }
                else if (cp.NamespaceURI.Equals(Namespaces.CONDITIONS))
                {
                    conditionsPart = cp;
                }
                else if (cp.NamespaceURI.Equals(Namespaces.COMPONENTS))
                {
                    componentsPart = cp;
                }
                else if (cp.NamespaceURI.Equals(Namespaces.QUESTIONS))
                {
                    questionsPart = cp;
                }
                else if (!cp.BuiltIn)
                {
                    log.Info("--> built-in");
                    cxp.Add(cp);
                }
            }

            if (xpathsPart == null || conditionsPart == null)
            {
                log.Warn("XPaths or Conditions part missing .. so won't create task pane.");
                return null;
            }

            //WedTaskPane taskPane =

            //taskPane.setupCcEvents(document);

            // Tell the docx its task pane
            Microsoft.Office.Tools.CustomTaskPane ctp
                = createCTP(document, cxp, xpathsPart, conditionsPart, questionsPart, componentsPart);
            document.GetVstoObject(Globals.Factory).Tag = ctp;
            // Want a 2 way association
            WedTaskPane wedTaskPane = (WedTaskPane)ctp.Control;
            wedTaskPane.associatedDocument = document;

            Document extendedDocument = Application.ActiveDocument.GetVstoObject(Globals.Factory);
            extendedDocument.Shutdown += new EventHandler(extendedDocument_Shutdown);

            return ctp;
        }


        void taskPane_VisibleChanged(object sender, EventArgs e)
        {
            log.Debug("ctp visible event fired");
            Ribbon.myInvalidate();
        }


        public Microsoft.Office.Tools.CustomTaskPane createCTP(Word.Document document, List<Office.CustomXMLPart> cxp,
            Office.CustomXMLPart xpathsPart,
            Office.CustomXMLPart conditionsPart,
            Office.CustomXMLPart questionsPart,
            Office.CustomXMLPart componentsPart
            )
        {
            string ptcName = "OpenDoPE " + Globals.ThisAddIn.Application.ActiveDocument.Name;

            WedTaskPane plutextTabbedControl = new WedTaskPane(cxp, xpathsPart, conditionsPart, questionsPart, componentsPart);
            //AvalonForm plutextTabbedControl = new AvalonForm();


            Microsoft.Office.Tools.CustomTaskPane myCustomTaskPane = this.CustomTaskPanes.Add(plutextTabbedControl, ptcName);
            plutextTabbedControl.ctp = myCustomTaskPane;
            log.Debug("Added to CustomTaskPanes");
            myCustomTaskPane.Width = 300;
            myCustomTaskPane.Visible = true;

            myCustomTaskPane.VisibleChanged += new EventHandler(taskPane_VisibleChanged);


            return myCustomTaskPane;
        }



        void app_DocumentChange()
        {
            // RemoveOrphanedTaskPanes();
        }

        void ThisAddIn_WindowSelectionChange(Word.Selection selection)
        {
            // Is there a custom task pane associated with this document?
            Word.Window wn = this.Application.ActiveWindow;
            log.Debug("In WSC, with active window: " + wn.Caption);

            WedTaskPane wtp = getTaskPane();
            if (wtp != null)
            {
                wtp.handleSelectionChange();
            }

        }

        public WedTaskPane getTaskPane()
        {

            if (Application.ActiveDocument == null) return null;

            Document extendedDocument = Application.ActiveDocument.GetVstoObject(Globals.Factory);

            if (extendedDocument !=null
                && extendedDocument.Tag != null
                && extendedDocument.Tag is Microsoft.Office.Tools.CustomTaskPane)
            {
                Microsoft.Office.Tools.CustomTaskPane ctp = (Microsoft.Office.Tools.CustomTaskPane)extendedDocument.Tag;

                // The important bit
                if (ctp != null 
                    && ctp.Control != null 
                    && ctp.Control is WedTaskPane)
                {
                    return ((WedTaskPane)ctp.Control);
                }
            }
            return null;
        }

/*
        public Microsoft.Office.Tools.CustomTaskPane getTaskPane(bool deleteOthers)
        {
            Word.Window wn = this.Application.ActiveWindow;
            string ptcName = "OpenDoPE " + Globals.ThisAddIn.Application.ActiveDocument.Name;

            log.Debug("active window: " + wn.Caption);

            Microsoft.Office.Tools.CustomTaskPane result = null;
            WedTaskPane wedTaskPane = null;
            List<Microsoft.Office.Tools.CustomTaskPane> ctpDeletes = new List<Microsoft.Office.Tools.CustomTaskPane>();

            foreach (Microsoft.Office.Tools.CustomTaskPane ctp in CustomTaskPanes)
            {

                if (ctp.Window == null)
                {
                    log.Debug("encountered obsolete ctp. removing...");
                    ctpDeletes.Add(ctp);
                    continue;
                }

                log.Debug("ctp window: " + ((Word.Window)ctp.Window).Caption);


                if (((Word.Window)ctp.Window) == wn
                    && ctp.Control is WedTaskPane)
                {
                    log.Debug("found ctp");

                    result = ctp;
                    wedTaskPane = (WedTaskPane)ctp.Control;

                    // Verify, since if a doc with a ctp attached is closed,
                    // the ctp is moved to another doc!


                    //if (wedTaskPane.windowCaption.Equals("OpenDoPE " + wn.Caption))
                    // Don't use that test, since the caption could be 
                    // "docname without extension (Read-Only)"

                    if (wedTaskPane.windowCaption.Equals(ptcName))
                    {
                        // All good
                    } else {

                        log.Debug("but its obsolete. removing...");
                        ctpDeletes.Add(ctp);
                        continue;
                    }

                    // ok, we know its the right one
                    //muc.handleSelection(selection);

                    break;
                }
                else
                {
                    log.Debug("..not ctp window");
                }
            }

            // Clean up orphans
            if (deleteOthers)
            {
                foreach (Microsoft.Office.Tools.CustomTaskPane ctp in ctpDeletes)
                {
                    removeTaskPane(ctp);
                }
            }

            return result;

        }
 */ 

        public void removeTaskPane(Microsoft.Office.Tools.CustomTaskPane ctp)
        {
            if (ctp == null) return;
            CustomTaskPanes.Remove(ctp);
        }

        //public bool isaPlutextDocOpen()
        //{

        //    foreach (Microsoft.Office.Tools.CustomTaskPane _ctp in
        //        Globals.ThisAddIn.CustomTaskPanes)
        //    {
        //        if (_ctp.Title.Contains("Plutext"))
        //        {
        //            return true;
        //        }
        //    }
        //    return false;
        //}


        //private void ThisAddIn_DocumentBeforeClose(Word.Document document, ref bool cancel)
        //{
        //    /*
        //     * Unfortunately this fires before the save dialog, so if the
        //     * user hits cancel, the timer etc is still shutdown.
        //     * 
        //     * See Carter & Lippert p258.
        //     * 
        //     * Have posted to MSDN VSTO forum
        //     * http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3465964&SiteID=1
        //     */

        //    // Avoid save prompt
        //    // this.InnerObject.Saved = true;

        //    WedTaskPane myControl = null;
        //    //Boolean foundNewCtp = false;
        //    foreach (Microsoft.Office.Tools.CustomTaskPane _ctp in
        //        Globals.ThisAddIn.CustomTaskPanes)
        //    {

        //        // FIXME: somehow there seem to be 2 of these?

        //        Word.Window ctpWindow = (Word.Window)_ctp.Window;
        //        if (_ctp.Title.Contains("OpenDoPE") &&
        //            ctpWindow == Globals.ThisAddIn.Application.ActiveWindow)
        //        {
        //            myControl = (WedTaskPane)_ctp.Control;

        //            // ok, now save the document
        //            document.Save();


        //            this.CustomTaskPanes.Remove(_ctp);
        //        }
        //    }

        //}

        //private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        //{
        //    System.Diagnostics.Trace.WriteLine("shutdown event");
        //    log.Info("Document closed");        
        //}

        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
        }

        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }
        
        #endregion
    }
}
